package com.slz3.business.frame.aop;

import com.slz3.business.frame.atomic.AtomicLockState;
import com.slz3.business.frame.util.VE;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import static java.util.stream.Collectors.toList;

/**
 * Created by jinxing on 2017/7/19.
 *
 * 针对：expand.*Atomic
 * 约定：方法名中 _$ 后半部分为锁的名字
 * 约定：支持批量锁 方法名中可以存在多个 _$ 每个间隔后面部分就是一个锁的名字
 * 约定：方法名称按 _$ 拆分后[0]是方法名称 从[1]开始不能为空 否则不加锁
 * 说明：获得这个锁的线程会等待持有这个锁的线程若干毫秒
 */
@Aspect
@Component
public class ModMenuAtomicAOP{

    /**
     * 模块菜单原子操作锁集合
     */
    private static final Map<String, Lock> atomicLockMap = new ConcurrentHashMap<>();

    /**
     * 根据锁名称获取锁，如果锁不存在则创建一个锁并返回此锁。
     * @param atomicLockName 锁名称
     * @return 锁
     */
    public synchronized static Lock getAtomicLock(String atomicLockName) {
        Lock atomicLock = atomicLockMap.get(atomicLockName);
        if (atomicLock == null) {
            atomicLock = new ReentrantLock();
            atomicLockMap.put(atomicLockName, atomicLock);
        }
        return atomicLock;
    }

    /**
     * 受影响的方法
     */
    @Pointcut("execution(public * com.slz3.business.module.*.*.steward.*Atomic.*(..))")
    public void pointcut() {
    }

    /**
     * 通用的锁/原子操作
     * <p> 如果未得到锁则返回结果为 null
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        // pjp.proceed()
        Object obj = null;
        // 锁及其持有状态
        List<AtomicLockState> atomicLockList = new ArrayList<>();
        // try finally
        try {
            /**
             * 获取当前线程所访问的方法的名称
             */
            Signature sig = pjp.getSignature();
            if (!(sig instanceof MethodSignature)) {
                throw new IllegalArgumentException("该注解只能用于方法");
            }
            MethodSignature msig = (MethodSignature) sig;
            Object target = pjp.getTarget();
            Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
            String methodName = currentMethod.getName();
            /**
             * 只处理受影响方法中，方法名包含 _$ 的方法
             * @param methodNameSplit [0]是方法名称 从[1]开始不能为空 否则不加锁
             */
            String[] methodNameSplit = methodName.split("_\\$");
            System.err.println("-----> split is null size: " + Arrays.stream(methodNameSplit).parallel().filter(s -> s.length() == 0).collect(toList()).size());
            if (VE.isNotEmpty(methodNameSplit)
                    && Arrays.stream(methodNameSplit).parallel().filter(s -> s.length() == 0).collect(toList()).size() == 0) {
                String[] atomicLockNames = Arrays.copyOfRange(methodNameSplit, 1, methodNameSplit.length);
                System.err.println("atomicLockNames: " + Arrays.asList(atomicLockNames));
                // 获取锁
                Arrays.stream(atomicLockNames).forEach(atomicLockName ->
                    atomicLockList.add(new AtomicLockState(this.getAtomicLock(atomicLockName), false))
                );
                // 持有锁
                for (AtomicLockState atomicLock : atomicLockList) {
                    atomicLock.setStatus(atomicLock.getLock().tryLock(6000, TimeUnit.MILLISECONDS));
                }
                // 锁全部被持有
                if (atomicLockList.parallelStream().filter(a -> !a.getStatus()).collect(toList()).size() == 0) {
                    System.err.println(Thread.currentThread().getName().concat(" AtomicAOP: 锁全部被持有 " + Arrays.toString(atomicLockList.toArray())));
                    obj = pjp.proceed();
                    System.err.println(Thread.currentThread().getName().concat(" AtomicAOP: 完成原子操作"));
                } else {
                    System.err.println(Thread.currentThread().getName().concat(" AtomicAOP: 超时（等待持有锁线程释放锁）"));
                }
            }
        } finally {
            // 存在持有锁
            if (atomicLockList.parallelStream().filter(a -> a.getStatus()).collect(toList()).size() > 0) {
                System.err.println(Thread.currentThread().getName().concat(" AtomicAOP: 存在持有锁"));
                atomicLockList.stream().forEach(a -> {
                    if (a.getStatus()) {
                        a.getLock().unlock();
                        System.err.println(Thread.currentThread().getName().concat(" AtomicAOP: 释放锁"));
                    }
                });
            }
            System.err.println(Thread.currentThread().getName().concat(" AtomicAOP: 模块菜单原子操作锁 Size: ".concat(Integer.toString(atomicLockMap.size()))));
        }
        System.err.println(Thread.currentThread().getName().concat(" AtomicAOP obj: " + obj));
        return obj;
    }
}
